// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.network.im;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.vc.core.VcConstants;
import com.x52im.rainbowchat.common.dto.cnst.UserProtocalsType;
import com.x52im.rbchat.logic.chat_friend.utils.MessageHelper;
import com.x52im.rbchat.utils.NotificationPromptHelper;

import net.x52im.mobileimsdk.android.event.ChatMessageEvent;
import net.x52im.rainbowav.sdk.iface.IReceiveEventListener;

import java.util.Observer;

/**
 * 与IM服务器的底层数据交互事件在此ChatTransDataEvent子类中实现即可。
 * <p>
 *     本类是MobileIMSDK的通用数据通信消息的回调事件接口实现类（接收的事件
 *     如：收到聊天数据事件 通知、服务端返回的错误信息事件通知等）。
 * </p>
 * <p>
 *     RainbowChat的IM通信底层是基于MobileIMSDK即时通讯框架实现，
 *     如需了解RainbowChat的通信层原理，详情请前往了解MobileIMSDK框架，
 *     地址是：http://www.52im.net/thread-52-1-1.html
 * </p>
 *
 * @author Jack Jiang
 * @since 1.0
 */
public class ChatMessageEventListener implements ChatMessageEvent, IReceiveEventListener
{
	private final static String TAG = ChatMessageEventListener.class.getSimpleName();

	private Context context;
	
	/** 视频聊天请求观察者. */
	private Observer viodeoChatRequestObserver = null;
	/** 实时语音聊天请求观察者【收到实时语音请求处理方式1】. */
	private Observer realTimeVoiceChatRequestObserver = null;
	/** 实时语音聊天请求观察者【收到实时语音请求处理方式2】. */
	private Observer realTimeVoiceChatRequestForNotIntChatUIObserver = null;
	
//	/** 新增消息的观察者：当前主要用于好友列表中的用户消息null;
	
	/** 音视频聊天呼出事件观察者（本观察者将在{@link ChatMessageEventListener}收到对方（视频聊天接收方）的“同意”或“不同意”的处理时被通知）.*/
	private Observer videoCallOutObserver = null;
	/** 音视频聊天呼入事件观察者（本观察者将在{@link ChatMessageEventListener}收到对方（视频聊天接收方）的“取消视频聊天请求”的处理时被通知）. */
	private Observer videoCallComeObserver = null;
	
	/** 实时语音聊天呼出事件观察者（本观察者将在{@link ChatMessageEventListener}收到对方（实时语音聊天接收方）的“同意”或“不同意”的处理时被通知）. */
	private Observer realTimeVoiceCallOutObserver = null;
	/** 实时语音聊天呼入事件观察者（本观察者将在{@link ChatMessageEventListener}收到对方（实时语音聊天接收方）的“取消实时语音聊天请求”的处理时被通知）. */
	private Observer realTimeVoiceCallComeObserver = null;
	
	/** 【用于礼品工具UI被实例化后的情况下的单独通知礼品工具UI的观察者】
	 * 收到好友发过来的礼品观察者对象.目前此观察者只要用于聊天界面中在收到礼品后能即时刷新“我的礼品包”的ui展现之用. */
	private Observer receivedGiftObserverForGiftToolsUI = null;
	/** 【用于礼品工具UI不管有没被实例化的情况下，都会去通知的观察者：比如显示一个toast提示收到了礼品等】
	 * 为何要独立用一个通用的观察者呢？因为receivedGiftObserverForGiftToolsUI只在所有礼品工具都被实例化后才被设置
	 * （为了提升界面构造性能，只在使用时构造之），那么在接收方没有打开过礼品工具的话岂不就是无法得到收到礼品提示了吗，所
	 * 以就有了此通用观察者，它会被保证首先被设置，从而即时给于收到礼品的提示哦！
	 * 目前此观察者只要用于聊天界面中在收到礼品后能即时刷新“我的礼品包”的ui展现之用. */
	private Observer receivedGiftObserverForCommonUI = null;
	
	public ChatMessageEventListener(Context context) {
		this.context = context;
	}

	/**
	 * MobileIMSDK框架收到普通消息的回调事件通知。
	 * <br>应用层可以将此消息进一步按自已的IM协议进行定义，
	 * 从而实现完整的即时通信软件逻辑。
	 *
	 * @param fingerPrintOfProtocal 当该消息需要QoS支持时本回调参数为该消息的特征指纹码，否则为null
	 * @param userid 消息的发送者id（MobileIMSDK框架中规定发送者id="0"即表示是由服务端主动发过的，否则
	 * 表示的是其它客户端发过来的消息）
	 * @param dataContent 消息内容的文本表示形式
	 */
	@Override
	public void onRecieveMessage(String fingerPrintOfProtocal, String userid, String dataContent, int typeu) {
		try {
			Log.d(TAG, "[typeu="+typeu+"] IM通道收到来自对象"+userid+"的数据dataContent="+dataContent+"，fingerPrintOfProtocal="+fingerPrintOfProtocal);
			
			final String msg = dataContent;

			// 是否是上线通知
			if(typeu == UserProtocalsType.MT01_OF_ONLINE_NOTIVICATION) {
				ChatMessageEventProcessor.processMT01(context, msg);
			}
			// 是否是下线通知
			else if(typeu == UserProtocalsType.MT02_OF_OFFLINE_NOTIVICATION) {
				ChatMessageEventProcessor.processMT02(msg);
			}
			// 【临时聊天消息：由服务端转发给接收人B的【步骤2/2】】
			else if(typeu == UserProtocalsType.MT43_OF_TEMP$CHAT$MSG_SERVER$TO$B) {
				ChatMessageEventProcessor.processMT43(context, fingerPrintOfProtocal, msg);
			}
			// 【群聊/世界频道聊天消息：由服务端转发给接收人B的【步骤2/2】】
			else if(typeu == UserProtocalsType.MT45_OF_GROUP$CHAT$MSG_SERVER$TO$B) {
				ChatMessageEventProcessor.processMT45(context, fingerPrintOfProtocal, msg);
			}
			// 【群聊系统指令：加群成功后通知被加群者（由Server发出）】通知接收人可能是在创建群或群建好后邀请进入的
			else if(typeu == UserProtocalsType.MT46_OF_GROUP$SYSCMD_MYSELF$BE$INVITE_FROM$SERVER) {
				ChatMessageEventProcessor.processMT46(context, msg);
			}
			// 【群聊系统指令：通用的系统信息给指定群员（由Server发出，指定群员接收）】
			else if(typeu == UserProtocalsType.MT47_OF_GROUP$SYSCMD_COMMON$INFO_FROM$SERVER) {
				ChatMessageEventProcessor.processMT47(context, userid, msg);
			}
			// 【群聊系统指令：群已被解散（由Server发出，除解散者外的所有人接收）】
			else if(typeu == UserProtocalsType.MT48_OF_GROUP$SYSCMD_DISMISSED_FROM$SERVER) {
				ChatMessageEventProcessor.processMT48_OF_GROUP$SYSCMD_DISMISSED_FROM$SERVER(context, userid, msg);
			}
			// 【群聊系统指令："你"被踢出群聊（由Server发出，被踢者接收） 】
			else if(typeu == UserProtocalsType.MT49_OF_GROUP$SYSCMD_YOU$BE$KICKOUT_FROM$SERVER) {
				ChatMessageEventProcessor.processMT49(context, userid, msg);
			}
			// 【群聊系统指令："别人"主动退出或被群主踢出群聊（由Server发出，其它群员接收）  】
			else if(typeu == UserProtocalsType.MT50_OF_GROUP$SYSCMD_SOMEONEB$REMOVED_FROM$SERVER) {
				ChatMessageEventProcessor.processMT50(context, userid, msg);
			}
			// 【群聊系统指令：群名被修改的系统通知（由Server发出，所有除修改者外的群员接收） 】
			else if(typeu == UserProtocalsType.MT51_OF_GROUP$SYSCMD_GROUP$NAME$CHANGED_FROM$SERVER) {
				ChatMessageEventProcessor.processMT51(context, msg);
			}
			// 【加好友错误提示】
			// 由服务端反馈给加好友发起人的错误信息(出错的可能是：该好友已经存在于我的好友列表中、插入好友请求到db中时出错等)
			else if(typeu == UserProtocalsType.MT06_OF_ADD_FRIEND_REQUEST_RESPONSE$FOR$ERROR_SERVER$TO$A) {
				Log.e(TAG, MessageHelper.parseAddFriendRequestResponse_for$error_server$to$a(msg));
				NotificationPromptHelper.showAddFriendRequest_RESPONSE$FOR$ERROR_SERVER$TO$ANotivication(context, MessageHelper.parseAddFriendRequestResponse_for$error_server$to$a(msg));
			}
			// 服务端通知在线被加好友者：收到了加好友请求
			else if(typeu == UserProtocalsType.MT07_OF_ADD_FRIEND_REQUEST_INFO_SERVER$TO$B) {
				ChatMessageEventProcessor.processMT07(context, msg);
			}
			// 新好友已成功被添加后由服务端发给在线用户对方的个人信息（此场景是被请求用户同意了加好友的请求时，由服务端把
			// 双方的好友信息及时交给对方（如果双方有人在线的话）），加入到本地好友列表中了后，就可以及时聊天了（如果对方此时在线的话）
			else if(typeu == UserProtocalsType.MT10_OF_PROCESS_ADD$FRIEND$REQ_FRIEND$INFO$SERVER$TO$CLIENT) {
				ChatMessageEventProcessor.processMT10(context, msg);
			}
			// 加好友被拒绝的实时消息(由服务端在B拒绝A的请求后实时通知A)
			else if(typeu == UserProtocalsType.MT12_OF_PROCESS_ADD$FRIEND$REQ_SERVER$TO$A_REJECT_RESULT) {
				ChatMessageEventProcessor.processMT12(context, msg);
			}

			//----------------------------------------------------------------------------------------------------- S
			// 处理视频聊天中：结束本次音视频聊天
			else if(typeu == UserProtocalsType.MT14_OF_VIDEO$VOICE_END$CHATTING) {
				ChatMessageEventProcessor.processMT14(context, msg);
			}
			// 处理视频聊天中：切换到纯音频聊天模式
			else if(typeu == UserProtocalsType.MT15_OF_VIDEO$VOICE_SWITCH$TO$VOICE$ONLY) {
				ChatMessageEventProcessor.processMT15(context, msg);
			}
			// 处理视频聊天中：切换回音频+视频聊天模式
			else if(typeu == UserProtocalsType.MT16_OF_VIDEO$VOICE_SWITCH$TO$VOICE$AND$VIDEO) {
				ChatMessageEventProcessor.processMT16(context, msg);
			}
			// ** v2.0后，自主产权的新视频聊天已使用方案2来实现“同意”指令的发出和处理，目前TransDataListener中的此指令仅用于处理AxxChat的视频聊天“同意”指令
			// 处理视频聊天呼叫中：已同意视频聊天请求(接收方B发给回发起方A)
			else if(typeu == UserProtocalsType.MT19_OF_VIDEO$VOICE$REQUEST_ACCEPT$TO$A) {
				String uid = MessageHelper.pareseVideoAndVoiceRequest$Accept_to$a(msg);
				processVideoChatRequest_forAccept(uid);
			}
			// 处理视频聊天呼叫中：拒绝视频聊天请求(接收方B发给回发起方A)
			else if(typeu == UserProtocalsType.MT20_OF_VIDEO$VOICE$REQUEST_REJECT$TO$A) {
				ChatMessageEventProcessor.processMT20(context, msg, videoCallOutObserver);
			}
			// 处理视频聊天呼叫中：收到视频聊天请求(由发起方A发给接收方B的)
			else if(typeu == UserProtocalsType.MT17_OF_VIDEO$VOICE$REQUEST_REQUESTING$FROM$A) {
				ChatMessageEventProcessor.processMT17(context, msg, viodeoChatRequestObserver);
			}
			// 处理视频聊天呼叫中：取消视频聊天请求(由发起方A发给接收方B的)
			else if(typeu == UserProtocalsType.MT18_OF_VIDEO$VOICE$REQUEST_ABRORT$FROM$A) {
				ChatMessageEventProcessor.processMT18(context, msg, videoCallComeObserver);
			}
			//----------------------------------------------------------------------------------------------------- E

			//-------------------------------------------------------------------- 实时语音协议处理 START
			// 处理实时语音聊天中：结束本次实时语音聊天
			else if(typeu == UserProtocalsType.MT35_OF_REAL$TIME$VOICE_END$CHATTING) {
				ChatMessageEventProcessor.processMT35(context, msg);
			}
			// 处理实时语音聊天呼叫中：拒绝实时语音聊天请求(接收方B发给回发起方A)
			else if(typeu == UserProtocalsType.MT34_OF_REAL$TIME$VOICE$REQUEST_REJECT$TO$A) {
				ChatMessageEventProcessor.processMT34(context, msg, realTimeVoiceCallOutObserver);
			}
			// 处理实时语音聊天呼叫中：请求实时语音聊天(由发起方A发给接收方B的)
			else if(typeu == UserProtocalsType.MT31_OF_REAL$TIME$VOICE$REQUEST_REQUESTING$FROM$A) {
				ChatMessageEventProcessor.processMT31(context, msg
						, realTimeVoiceChatRequestObserver, realTimeVoiceChatRequestForNotIntChatUIObserver);
			}
			// 处理实时语音聊天呼叫中：取消实时语音聊天请求(由发起方A发给接收方B的)
			else if(typeu == UserProtocalsType.MT32_OF_REAL$TIME$VOICE$REQUEST_ABRORT$FROM$A) {
				ChatMessageEventProcessor.processMT32(context, msg, realTimeVoiceCallComeObserver);
			}
			//-------------------------------------------------------------------- 实时语音协议处理 END
			
			// 普通一对一聊天消息（聊天消息可能是：文本、图片、语音留言、礼物等）
			else if(typeu == UserProtocalsType.MT03_OF_CHATTING_MESSAGE) {
				ChatMessageEventProcessor.processMT03(context, userid, fingerPrintOfProtocal, msg, receivedGiftObserverForGiftToolsUI, receivedGiftObserverForCommonUI);
			} else {
				Log.e(TAG, "【非法】来自"+userid+"的未定义typeu=" +typeu+"数据包，无法继续处理，请核实协议定义！！！");
			}
		} catch (Exception e) {
			Log.w(TAG, e);
		}
	}

	/**
	 * MobileIMSDK框架服务端反馈的出错信息回调事件通知。
	 *
	 * @param errorCode 错误码，定义在常量表：
	 *     http://docs.52im.net/extend/docs/api/mobileimsdk/server/constant-values.html#net.openmob.mobileimsdk.server.protocal.ErrorCode.ForS.RESPONSE_FOR_UNLOGIN 中
	 * @param errorMsg 描述错误内容的文本信息
	 */
	@Override
	public void onErrorResponse(int errorCode, String errorMsg) {
		Log.e(TAG, "收到了服务端返回的错误信息：errorCode="+errorCode+", errorMsg="+errorMsg);
	}

	public void setViodeoChatRequestObserver(Observer viodeoChatRequestObserver) {
		this.viodeoChatRequestObserver = viodeoChatRequestObserver;
	}

	public void setRealTimeVoiceChatRequestForNotIntChatUIObserver(Observer realTimeVoiceChatRequestForNotIntChatUIObserver) {
		this.realTimeVoiceChatRequestForNotIntChatUIObserver = realTimeVoiceChatRequestForNotIntChatUIObserver;
	}

	public void setRealTimeVoiceChatRequestObserver(Observer realTimeVoiceChatRequestObserver) {
		this.realTimeVoiceChatRequestObserver = realTimeVoiceChatRequestObserver;
	}

	public void setVideoCallOutObserver(Observer videoCallOutObserver) {
		this.videoCallOutObserver = videoCallOutObserver;
	}

	public void setVideoCallComeObserver(Observer videoCallComeObserver) {
		this.videoCallComeObserver = videoCallComeObserver;
	}

	public void setRealTimeVoiceCallOutObserver(Observer realTimeVoiceCallOutObserver) {
		this.realTimeVoiceCallOutObserver = realTimeVoiceCallOutObserver;
	}

	public void setRealTimeVoiceCallComeObserver(Observer realTimeVoiceCallComeObserver) {
		this.realTimeVoiceCallComeObserver = realTimeVoiceCallComeObserver;
	}

	public void setReceivedGiftObserverForGiftToolsUI(Observer receivedGiftObserver) {
		this.receivedGiftObserverForGiftToolsUI = receivedGiftObserver;
	}

	public void setReceivedGiftObserverForCommonUI(Observer receivedGiftObserverForCommonUI) {
		this.receivedGiftObserverForCommonUI = receivedGiftObserverForCommonUI;
	}

	//***************************************************************************** 新音视频框架事件消息处理 START
	/**
	 * 处理接收方“同意”实时语音聊天的最终处理方法.
	 *
	 * @param uid
	 */
	private void processRealTimeVoiceInteractProtocal_forAccept(String uid) {
		if(uid == null)
			return;

		// 通知观察者：对方B已经同意了实时语音聊天请求
		if(realTimeVoiceCallOutObserver != null) {
			// 简单传一个整型作为“同意”的标识常量
			realTimeVoiceCallOutObserver.update(null, 1);
		} else {
			Log.d(TAG, "收到了来自"+uid+"的同意实时语音聊天请求，但realTimeVoiceCallOutObserver==null，无法进入实时语音聊天!");
		}
	}

	/**
	 * 处理接收方“同意”实时视频聊天的最终处理方法.
	 *
	 * @param uid
	 */
	private void processVideoChatRequest_forAccept(String uid) {
		if(uid == null)
			return;

		// 通知观察者：对方B已经同意了视频聊天请求
		if(videoCallOutObserver != null) {
			// 简单传一个整型作为“同意”的标识常量
			videoCallOutObserver.update(null, 1);
		} else {
			// 边界问题处理：当用户B同意了视频聊天，但A这端却按了home键（或其它操作），则此次也还是无法连接视频，A这端的这种异常动作应该要想办法通知B？否则B启不是一直在等
			Log.d(TAG, "收到了来自"+uid+"的同意视频聊天请求，但videoCallOutObserver==null，无法进入视频聊天!");
		}
	}

	// ## * 关于基于新框架的实时音频和视频“同意”指令的发出和接收方案分为方案1（v2.0日前）、方案2（v2.0日后）两种。
	// ## * v2.0 及以后使用的是方案2，方案1暂时停用（不排除以后去掉的可能）。
	// ## * 什么是方案1？什么是方案2？方案1即使用KChat自身的UDP消息来实现“同意指令”，方案2即利用新音视频框架自已的消息指令机制。
	// ## * 方案1有何不妥？方案1带来的风险和问题是：同意指令是脱离音视频框架的，导致被叫方（从音视频框架的角度讲）发出receive指令可能先于request指令的到达！
	// ## * 方案1的好处是不丢包，方案2的好处是能保证同意指令发出且被收到时被叫方才会发receive指令（即request指令肯定是先于receive执行的）。
	// ## * 方案2的坏处是丢包。
	// ## * 实施方案2后，目前看来，从网络指令角度来讲，只要音视频框架自已的指令不丢包，就肯定能正常连通！
	// ## v2.0基于新框架的实时音频和视频“同意”指令的发出和接收由框架自已的指令完成
	// @see vc1026/FriendChattingActivity.java
	private Handler vvEventHandler = new Handler(Looper.myLooper()){
        @Override
        public void handleMessage(Message msg) {
            Bundle data = msg.getData();
            long fromAccount = data.getLong("fromAccount",0);
            switch(msg.what){
                //收到视频请求（主叫方已“同意”了）
                case VcConstants.TYPE_REQUEST_VIDEO_BY_FRIEND: {
                	System.out.println("NTMD:2同意了！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！");
                	//** v2.0后【实时视频】“同意”指令的发出和接收方案2之【第2步：被叫方（相对音视频框架而言）收到“同意”后的处理】
                	Log.d(TAG, "《VV》《VEDEO》收到来自"+fromAccount+"的实时视频聊天\"同意\"指令。。。");
                	if(fromAccount != 0) {
                		String uid = String.valueOf(fromAccount);
                		processVideoChatRequest_forAccept(uid);
                	} else {
                		Log.e(TAG, "《VV》《VEDEO》收到来自"+fromAccount+"的实时视频聊天\"同意\"指令，可是fromAccount=0！");
                	}
                    break;
                }
                //收到语音请求（主叫方已“同意”了）
                case VcConstants.TYPE_REQUEST_AUDIO_BY_FRIEND: {
                	//** v2.0后【实时音频】“同意”指令的发出和接收方案2之【第2步：被叫方（相对音视频框架而言）收到“同意”后的处理】
                	Log.d(TAG, "《VV》《VOICE》收到来自"+fromAccount+"的实时语音聊天\"同意\"指令。。。");
                	if(fromAccount != 0) {
                		String uid = String.valueOf(fromAccount);
                		processRealTimeVoiceInteractProtocal_forAccept(uid);//vvdto);
                	} else {
                		Log.e(TAG, "《VV》《VOICE》收到来自"+fromAccount+"的实时语音聊天\"同意\"指令，可是fromAccount=0！");
                	}
                    break;
                }
            }
        }
    };

	@Override
	public void onReceiveEvent(int eventId, long fromAccount, byte[] detail) {
		Message msg = new Message();
        msg.what = eventId;
        Bundle bundle = new Bundle();
        bundle.putLong("fromAccount",fromAccount);
        bundle.putByteArray("detail",detail);
        msg.setData(bundle);
        vvEventHandler.sendMessage(msg);
	}
	//***************************************************************************** 新音视频框架事件消息处理 END
}
